package com.yifushidai.utils;

/**
 * 自定义协议
 */
public class CustomPackUtils {

    public static final String HEAD_HEX = "55AA";//消息头 HEX字符串
    public static final byte[] HEAD = new byte[]{(byte) 0x55, (byte) 0xAA};//0xAA55 2字节
    public static final int HEAD_OFFSET = HEAD.length;//帧头：起始标识位，表示消息帧的开始（低字节在前）
    public static final int LEN_OFFSET = 2;//有效载荷长度：有效载荷PAYLOAD的长度
    public static final int SOURCE_OFFSET = 1;//数据源：采用设备ID表示数据的来源
    public static final int DEST_OFFSET = 1;//数据目的地：采用设备ID表示数据的目的地，如果数据目的地不是自身则直接转发本数据帧到目的设备
    public static final int MSG_OFFSET = 1;//消息ID：接收端根据这个编号来确定有效载荷里的消息类容（消息类型）
    public static final int HEADER_LENGTH = HEAD_OFFSET + LEN_OFFSET + SOURCE_OFFSET + DEST_OFFSET + MSG_OFFSET;//消息头总长
    public static final int PAYLOAD_OFFSET = 1308;//有效载荷：消息数据（多字节数据低字节在前）
    public static final int XOR_OFFSET = 1;//异或校验位 = 有效载荷长度^数据源^数据目的地^消息ID^有效载荷 的异或值

    public static final byte SERVER_DEVICE_ID = (byte) 0xA0;//云端服务器 设备ID
    public static final byte IDCARD_READER_DEVICE_ID = (byte) 0xA1;//身份证读卡器 设备ID
    public static final byte UNIQUE_MAC_DEVICE_ID = (byte) 0xA2;//联网盒 设备ID

    public static final byte MAC_INIT_MSG_ID = (byte) 0xC8;//设备初始化 消息ID
    public static final byte SERVER_RESPONSE_MSG_ID = (byte) 0xC9;//服务器应答 消息ID
    public static final byte KA_REQUEST_MSG_ID = (byte) 0xCA;//心跳请求 消息ID
    public static final byte KA_RESPONSE_MSG_ID = (byte) 0xCB;//心跳响应 消息ID
    public static final byte IDCARD_LOAD_MSG_ID = (byte) 0xCC;//身份证录入 消息ID
    public static final byte DEVICE_RESPONSE_MSG_ID = (byte) 0xCD;//硬件端应答 消息ID
    public static final byte IDCARD_UNPACK_MSG_ID = (byte) 0xCE;//身份证解析 消息ID
    public static final byte IDCARD_BIND_MSG_ID = (byte) 0xCF;//身份证绑定 消息ID
    public static final byte IDCARD_UNBIND_MSG_ID = (byte) 0xD0;//身份证解绑 消息ID
    public static final byte ROOM_LOGOUT_MSG_ID = (byte) 0xD1;//房间注销 消息ID
    public static final byte LOCK_POWER_MSG_ID = (byte) 0xD2;//锁电量 消息ID
    public static final byte LOCK_OPEN_MSG_ID = (byte) 0xD3;//开门结果 消息ID

    public static final int OFF_LINE = 0;
    public static final int ON_LINE = 1;
    public static final String FAIL_HEX_CMD = "00";//应答00 指令
    public static final String SUCCESS_HEX_CMD = "$recived$";//应答$recived$ 指令

    public static final String MAC_INIT_HEX_CMD = "FFFF930B3147323973342443";//设备初始化 指令
    public static final String MAC_INIT_HEX_CMD2 = "AAFF930B3147323973342245";//设备初始化 指令
    public static final String RESPONSE_SUCCESS_HEX_CMD = "01";//应答成功 指令
    public static final String RESPONSE_FAIL_HEX_CMD = "00";//应答失败 指令
    public static final String KA_REQUEST_HEX_CMD = "AA";//心跳请求 指令
    public static final String KA_RESPONSE_HEX_CMD = "BB";//心跳响应 指令
    public static final String IDCARD_LOAD_HEX_CMD = "A1";//身份证录入 指令
    public static final String IDCARD_UNPACK_HEX_CMD = "FAFBFCFD0405008B73248D1F4F200020002000200020002000200020002000200020002000310030003100310039003800360030003900300031001062FD90025E29599C5EB0653A534E5333967F5EFD900A4E5788310033003800F753310036000B68310055534351310032007C693100320030003100F75320002000200020003300340032003400320035003100390038003600300039003000310030003200310033001062FD90025E6C51895B405C29599C5EB0653A530652405C2000200020003200300031003800300031003200320032003000330038003000310032003200200020002000200020002000200020002000200020002000200020002000200020002000574C66007E00320000FF851D5151513E710DD564F3753D2A7237BED8D01C05CEDB43FCBF2DCAEB132E6AA05DBC77B47AF385A8AE51D32B22AC5248DB4EF159DEB78006D89469BDDD713E95592F8D156767F43BD94945A9E0FFB1AED65251515A3E80F8EE45FDD288D9E97540F1465DA9E0CB7EE95990495DF20495C4765C634FFFF71A264C53A1A88ABC80159E1D7233A853E4B8C4F2386634BD3FF0B39C523C612F4228C582315E0FE2F16F2B0D5FE1C3CE12C7DBA392EA32FBA0120A4A9A11A25645D52DA16B27365EA4EB6C6B06852F3C7029CA7DE409939045C19126D82E3F2A577BD77BEA68CBA1D794BD1156E29FFC4A9FFB1D25197D1B61968643B7B5BBE99F7B713CC117D8EB1251FBDCAC6DA9F45D1B6E86435F753B65400CA26A0CA66758ADBEBADD2A905829173C2D45D2E36BB80A607A35D3945F0F57835AFEB9F206D96AB952977087B596C5FDFBA3A3ABB4A319F1D5CF2BA3A455FCCE844135A3B14015C73A045AAAF617B617512678C5B76521C9DC2A9E28F604BDF7725DD9CD1E4276A3A0944945FFFCE4943C9293C013101204F660A5331D3F9531282766CBD7A1F8328330B33146E0134C90B028D06D1E61F73EE2B6AC41AD1313B16A199A78136D93BB64171402C0A6FD136B519EABD5112D2F5F314B8B7DDE387ECABEBA3C16E40F6D01ED0753BEBDC4E2A1CC79B79270EE2BEF1BD17CDF6AC7B0B8EC55AB70F70956D1752B168A8D61087BE7AF24E00C67EDF890704DAC89E31EAB6452FF6E823CF37A9EC608C2D3026948A4F20FCDC9D85AA9A58E17F0BB919820CA1CBA52CCF75F4D297C4E45164CECD1C099DC6706A34E1B31ADC49C3532D7E1461B1139B1082E6EBDE87503806C4C855E8A33F86827656A6BA85511199578A52FDF2FF52FC4B35BAE51BA1D9EC43FF9E0A37F0DB7546C9A2454130BB8A06B9EEE9BBFF50408783FB3555FBBAF05D0725B5AC7F6677532E432178F9C95E0464BF981371D476A0016B2A22B6B986B6CA8FF45F9839CE71977FDA190A02DF858E40492525E80A3E5A066A8691F71EBB5546550D1FE8FCF77E652C243E22C2242C1B308D2D9619604B918032CB912B881B46A194CA487C07C4F3701A6C3EBF9DE477CCC0C52D08A4686F537B64FDAAC76B94BD0045A324D785D4F36671201137B3D7A3F19661BF81074BFE3A8E4E53D0F59BAF3E5BF2C1C79E27FCED89D732146FE5818967C1A825A3EABD6D60D413562CBAE51C37B37847D08632CF4F33CFE01B3DA5DC80B464A77D9C6019028CFC9672BF8DF3B5D891CD106744AF35EB2267B9FD1B69494B10FB3495EADCC3351B37A7CC1C36F9E70302F5A3E88DE06A0FDE578E451FFCC2844444DBE90CF4E5792CFAC3E5F9B1042F1592C0D0D8B9E40098224C691CEFD16EAF97CB8473224FA78B8DB079D41D433EB26612105C68E1291A38BCBEEBBFAFBFCFD0E0000322E925F0007A9AA90004BBB";//身份证绑定 指令
    public static final String IDCARD_UNPACK_HEX_CMD2 = "FAFBFCFD040500554F00659B6D200020002000200020002000200020002000200020002000310030003100310039003900330031003100300034007F89895B025E6854F381BF53CC5BC14E614ECC5BC14E51672D4EC35F5788320038003900F75320002000200020002000200020002000200020002000200020002000200020003600310030003100320034003100390039003300310031003000340034003500310032006854F381BF536C51895B405C2000200020002000200020002000200020003200300031003000300037003200300032003000320030003000370032003000200020002000200020002000200020002000200020002000200020002000200020002000574C66007E00320000FF85195151513E710DD564F304D1AF7837A655E2D9D5D039AE512A35396D633061CA6FD11B620AD6D143E77CFD073F90F43343F7CB25A728897237F1D6A7D6EFFC9FFC45E9731E64B31A247951AEDA5251515A3E81ABD91546249D3BEE16E4CC70F62B028103A13D60B0F976C02D2A0F19BD5D79792C284FED356582CD8C157490DFD9488BEB0724A90B8A2450EECC2C940586F9A4A5745E76666A454838D0DB0EFE4DC7FE7AF98C26FAF4735EC4E47C5BC63E11B87C7E4D6AA4089A6362C0654900DBFCDAAC2FA4A9B547B4B4953946B17C78F3AA18D87B271A1E07E4C45673E0FD3051FA90E508849D23860A69065010F6B7211EEF976710FE1729B10DC7A43D0997CBEF45F0EF6D938A1CFF452A38FE96E8E7859A29F64FF0E425267E631875A2B661EF9887E243310059A495ED1173FBF01C4D6FD1846A136390E140B12900F91E30E784D1C19863E1F75932BB16296F7D512EDD71793F80647313769BAA8868B5BE6F97993F794F6FF518C4C549205E9647AE51B6EB25115E5030AB6EB5C9E598FC5EE4479DDD886823C35C18914C48CB53FBF24B4A892123AE5148F44E48D4FD8AA6EBAADB11BCE9722D5114700796340B7DF687BC3A174ACE3CBFC38E9C9BC82C1DC672A62487D9CA30EC16AA73C6A20C6729B8BD6EDDFC6FF1277F93BED3723420FC19DE83F1B8710F7E04288C8CA791F10CB7AE51ED769F5C69C2DFEB0F4564CC285CCBBFAB751523052FA275F9E0241CA2526DBD80CF4C5E330EBFD35E9E8C0DEFAE51D1C7181128BAF9B47471BE40A82314E420E54F57FA57959017D6A218564887A01940C80DBC29F3F18D5914A482CE3B30EB1B2A95B4B3A9E7EDA242F83446F6BD2A577A5721A977970DDB3FF368CCAE5125C3739B690C8A18E198BC25942C890A9937D6530D168D78E5AB3D58375A7AE0475D7D514FA1D7561277559EDC9E9C695544B9C42EE148D12659F95C3014654645DD3D2C977C8876ED761BBC0C2CB83C3048D39B4243CC6647BEF3232A468C6FB6FEF165805D6C4413E29417C1A4BE3B9DF4731DAF0583C1ABF4D116FC8E41351DAD40BCFD1360BF4849F61AB51D1E6A024C267B3112121CFF3F5069B03A3E499665146B44C013F58F42067A7B4EF908F01C6EC3974BD73BB3913A5D4936DB4CEF02AD33EC012FE905E77DCD31DFEBD87D426C23C98B2C6364173D6C392F01E81F24935A3E31116277AA985C9DE4293A31C9CAA1665E25370CA11022EAD0DCE213FCC5FC2C377AF12778415115DE67CB78BA10D153E435BF6949106BE08F261CF39A593972F4CA4CCC3C1005E5432026A51A00EDBC5A3E8B62462328828F2544A6E0D75158872A28D1A242A44A2DFAA42F2B773CDB5DCF4130F2B042DF88839FA980E5BCC3E970CD0247BE1B2EDA442156FA3DBCDB27D1E701CC3A419C7144F4BBFAFBFCFD0E00003125C602C00512A8900031BB";//身份证绑定 指令
    public static final String IDCARD_BIND_HEX_CMD = "D0322E925F0007A9AA";//身份证绑定 指令
    public static final String IDCARD_UNBIND_HEX_CMD = "D1";//身份证解绑 指令
    public static final String ROOM_LOGOUT_HEX_CMD = "A2";//房间注销 指令
    public static final String LOCK_POWER_NORMAL_HEX_CMD = "01";//锁电量正常 指令
    public static final String LOCK_POWER_LOWER_HEX_CMD = "00";//锁电量低 指令
    public static final String LOCK_OPEN_SUCCESS_HEX_CMD = "D9";//开门成功 指令
    public static final String LOCK_OPEN_FAIL_HEX_CMD = "00";//开门失败 指令

    /** 时间格式(yyyy-MM-dd) */
    public static final String DATE_PATTERN = "yyyy-MM-dd";
    /** 时间格式(yyyy-MM-dd HH:mm:ss) */
    public static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";

    public static void main(String[] args) {
        //设备初始化
        byte[] mac_init = CustomPackUtils.pack(MAC_INIT_HEX_CMD, IDCARD_READER_DEVICE_ID, SERVER_DEVICE_ID, MAC_INIT_MSG_ID);
        System.out.println("<<< 读卡器 设备初始化：" + HexUtils.bytesToHexString(mac_init));
        //服务器应答
        byte[] server_response_success = CustomPackUtils.pack(RESPONSE_SUCCESS_HEX_CMD, SERVER_DEVICE_ID, IDCARD_READER_DEVICE_ID, SERVER_RESPONSE_MSG_ID);
        System.out.println(">>> 读卡器 服务器应答：" + HexUtils.bytesToHexString(server_response_success));
        //心跳请求
        byte[] ka_request = CustomPackUtils.pack(KA_REQUEST_HEX_CMD, IDCARD_READER_DEVICE_ID, SERVER_DEVICE_ID, KA_REQUEST_MSG_ID);
        System.out.println("<<< 读卡器 心跳请求  ：" + HexUtils.bytesToHexString(ka_request));
        //心跳响应
        byte[] ka_response = CustomPackUtils.pack(KA_RESPONSE_HEX_CMD, SERVER_DEVICE_ID, IDCARD_READER_DEVICE_ID, KA_RESPONSE_MSG_ID);
        System.out.println(">>> 读卡器 心跳响应  ：" + HexUtils.bytesToHexString(ka_response));
        //身份证录入
        byte[] idcard_load = CustomPackUtils.pack(IDCARD_LOAD_HEX_CMD, SERVER_DEVICE_ID, IDCARD_READER_DEVICE_ID, IDCARD_LOAD_MSG_ID);
        System.out.println(">>> 读卡器 身份证录入：" + HexUtils.bytesToHexString(idcard_load));
        //硬件端应答
        byte[] device_response_fail = CustomPackUtils.pack(RESPONSE_FAIL_HEX_CMD, IDCARD_READER_DEVICE_ID, SERVER_DEVICE_ID, DEVICE_RESPONSE_MSG_ID);
        System.out.println("<<< 读卡器 硬件端应答：" + HexUtils.bytesToHexString(device_response_fail));
        //身份证解析
        byte[] idcard_unpack = CustomPackUtils.pack(IDCARD_UNPACK_HEX_CMD, IDCARD_READER_DEVICE_ID, SERVER_DEVICE_ID, IDCARD_UNPACK_MSG_ID);
        System.out.println("<<< 读卡器 身份证解析：" + HexUtils.bytesToHexString(idcard_unpack));
        //身份证解析
        byte[] idcard_unpack2 = CustomPackUtils.pack(IDCARD_UNPACK_HEX_CMD2, IDCARD_READER_DEVICE_ID, SERVER_DEVICE_ID, IDCARD_UNPACK_MSG_ID);
        System.out.println("<<< 读卡器 身份证解析：" + HexUtils.bytesToHexString(idcard_unpack2));

        System.out.println();

        //设备初始化
        byte[] mac_init2 = CustomPackUtils.pack(MAC_INIT_HEX_CMD2, UNIQUE_MAC_DEVICE_ID, SERVER_DEVICE_ID, MAC_INIT_MSG_ID);
        System.out.println("<<< 联网盒 设备初始化：" + HexUtils.bytesToHexString(mac_init2));
        //服务器应答
        byte[] server_response_success2 = CustomPackUtils.pack(RESPONSE_SUCCESS_HEX_CMD, SERVER_DEVICE_ID, UNIQUE_MAC_DEVICE_ID, SERVER_RESPONSE_MSG_ID);
        System.out.println(">>> 联网盒 服务器应答：" + HexUtils.bytesToHexString(server_response_success2));
        //心跳请求
        byte[] ka_request2 = CustomPackUtils.pack(KA_REQUEST_HEX_CMD, UNIQUE_MAC_DEVICE_ID, SERVER_DEVICE_ID, KA_REQUEST_MSG_ID);
        System.out.println("<<< 联网盒 心跳请求  ：" + HexUtils.bytesToHexString(ka_request2));
        //心跳响应
        byte[] ka_response2 = CustomPackUtils.pack(KA_RESPONSE_HEX_CMD, SERVER_DEVICE_ID, UNIQUE_MAC_DEVICE_ID, KA_RESPONSE_MSG_ID);
        System.out.println(">>> 联网盒 心跳响应  ：" + HexUtils.bytesToHexString(ka_response2));
        //硬件端应答
        byte[] device_response_success = CustomPackUtils.pack(RESPONSE_SUCCESS_HEX_CMD, UNIQUE_MAC_DEVICE_ID, SERVER_DEVICE_ID, DEVICE_RESPONSE_MSG_ID);
        System.out.println("<<< 联网盒 硬件端应答：" + HexUtils.bytesToHexString(device_response_success));
        //身份证绑定
        byte[] idcard_bind = CustomPackUtils.pack(IDCARD_BIND_HEX_CMD, SERVER_DEVICE_ID, UNIQUE_MAC_DEVICE_ID, IDCARD_BIND_MSG_ID);
        System.out.println(">>> 联网盒 身份证绑定：" + HexUtils.bytesToHexString(idcard_bind));
        //身份证解绑
        byte[] idcard_unbind = CustomPackUtils.pack(IDCARD_UNBIND_HEX_CMD, SERVER_DEVICE_ID, UNIQUE_MAC_DEVICE_ID, IDCARD_UNBIND_MSG_ID);
        System.out.println(">>> 联网盒 身份证解绑：" + HexUtils.bytesToHexString(idcard_unbind));
        //房间注销
        byte[] room_logout = CustomPackUtils.pack(ROOM_LOGOUT_HEX_CMD, SERVER_DEVICE_ID, UNIQUE_MAC_DEVICE_ID, ROOM_LOGOUT_MSG_ID);
        System.out.println(">>> 联网盒 房间注销  ：" + HexUtils.bytesToHexString(room_logout));
        //锁电量
        byte[] lock_power = CustomPackUtils.pack(LOCK_POWER_LOWER_HEX_CMD, UNIQUE_MAC_DEVICE_ID, SERVER_DEVICE_ID, LOCK_POWER_MSG_ID);
        System.out.println("<<< 联网盒 锁电量    ：" + HexUtils.bytesToHexString(lock_power));
        //开门结果
        byte[] lock_open = CustomPackUtils.pack(LOCK_OPEN_SUCCESS_HEX_CMD, UNIQUE_MAC_DEVICE_ID, SERVER_DEVICE_ID, LOCK_OPEN_MSG_ID);
        System.out.println("<<< 联网盒 开门结果  ：" + HexUtils.bytesToHexString(lock_open));

//        System.out.println(HexUtils.bytesToHexString(unpack(mac_init)));
    }

    /**
     * 数据打包
     *
     * @param payloadHexString 有效载荷16进制字符串
     * @param sourceByte       数据源
     * @param destByte         数据目的地
     * @param msgByte          消息ID
     * @return 请求数据完整包
     */
    public static byte[] pack(String payloadHexString, byte sourceByte, byte destByte, byte msgByte) {
        byte[] payloadBytes = HexUtils.hexStringToBytes(payloadHexString);

        byte[] lenBytes = HexUtils.int2Bytes(payloadBytes.length);

        int mainPackageLen = HEADER_LENGTH + payloadBytes.length + XOR_OFFSET;
        byte[] mainPackage = new byte[mainPackageLen];
        System.arraycopy(HEAD, 0, mainPackage, 0, HEAD_OFFSET);
        System.arraycopy(lenBytes, 0, mainPackage, HEAD_OFFSET, LEN_OFFSET);
        mainPackage[HEAD_OFFSET + LEN_OFFSET] = sourceByte;
        mainPackage[HEAD_OFFSET + LEN_OFFSET + SOURCE_OFFSET] = destByte;
        mainPackage[HEAD_OFFSET + LEN_OFFSET + SOURCE_OFFSET + DEST_OFFSET] = msgByte;
        System.arraycopy(payloadBytes, 0, mainPackage, HEADER_LENGTH, payloadBytes.length);
        //生成异或校验位
        byte[] xorPackage = new byte[mainPackageLen - XOR_OFFSET - HEAD_OFFSET];
        System.arraycopy(mainPackage, HEAD_OFFSET, xorPackage, 0, xorPackage.length);
        mainPackage[mainPackageLen - XOR_OFFSET] = generateXorByte(xorPackage);

//        System.out.println("数据打包:" + HexUtils.bytesToHexString(mainPackage));

        return mainPackage;
    }

    /**
     * 数据解包
     *
     * @param mainPackage 请求数据完整包
     * @return 有效载荷(PAYLOAD)
     */
    public static byte[] unpack(byte[] mainPackage) {
        //异或位校验
        byte xor = mainPackage[mainPackage.length - XOR_OFFSET];
        byte[] xorPackage = new byte[mainPackage.length - XOR_OFFSET - HEAD_OFFSET];
        System.arraycopy(mainPackage, HEAD_OFFSET, xorPackage, 0, xorPackage.length);
//        System.out.println(xor);
//        System.out.println(generateXorByte(xorPackage));
        if (xor != generateXorByte(xorPackage)) return null;

        byte[] lenBytes = new byte[LEN_OFFSET];
        System.arraycopy(mainPackage, HEAD_OFFSET, lenBytes, 0, LEN_OFFSET);
        int payloadLen = HexUtils.bytes2Int(lenBytes);
//        short payloadLen = (short) (mainPackage[HEAD_OFFSET] | mainPackage[HEAD_OFFSET + 1] << 8);
        byte sourceByte = mainPackage[HEAD_OFFSET + LEN_OFFSET];
        byte destByte = mainPackage[HEAD_OFFSET + LEN_OFFSET + SOURCE_OFFSET];
        byte msgByte = mainPackage[HEAD_OFFSET + LEN_OFFSET + SOURCE_OFFSET + DEST_OFFSET];
        byte[] payloadBytes = new byte[payloadLen];
        System.arraycopy(mainPackage, HEADER_LENGTH, payloadBytes, 0, payloadLen);

//        System.out.println("数据解包:" + HexUtils.bytesToHexString(payloadBytes));

        return payloadBytes;
    }

    private static byte generateXorByte(byte[] xorPackage) {
        byte xorByte = xorPackage[0];
        for (int i = 1; i < xorPackage.length; i++) {
            xorByte ^= xorPackage[i];
        }
        return xorByte;
    }

}
